/**
    list.c - Boring list functions.
    Copyright (C) 2009  Abram Magner

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <stdio.h>
#include <stdlib.h>

#include "list.h"

//=============================================================================
//=============================================================================
list *new_list (void)
{
  list *retval = NULL;

  retval = malloc (sizeof (list));
  if (retval == NULL) return (NULL);
  retval->head = retval->tail = NULL;
  retval->length = 0;
  return (retval);
}

//=============================================================================
//=============================================================================
void free_list (list *l)
{
  list_node *iter = NULL, *next = NULL;
  iter = list_get_iter (l);
  while (list_iter_hasmore (iter))
    {
      next = iter->next;
      free (iter);
      iter = next;
    }
  free (l);
}

//=============================================================================
//=============================================================================
void free_list_and_data (list *l)
{
  list_iterate (l, free);
  free_list (l);
}

//=============================================================================
// Insert the given data at position index in l.
//=============================================================================
int list_insert (list *l, int index, void *data)
{
  list_node *newnode = NULL, *prev = NULL;

  if (index < 0 || index > l->length) return (-1);
  if ((newnode = malloc (sizeof (list_node))) == NULL) return (-1);
  newnode->data = data;

  prev = list_get_node (l, index - 1);
  newnode->prev = prev;
  // Vanilla insertion.
  if (prev != NULL)
    {
      newnode->next = prev->next;
      prev->next = newnode;
    }
  // We have a new head.
  else
    {
      newnode->next = l->head;
      if (l->head != NULL) l->head->prev = newnode;
      l->head = newnode;
    }
  if (newnode->next == NULL) l->tail = newnode;
  l->length++;

  return (0);
}


//=============================================================================
//=============================================================================
void *list_get (list *l, int index)
{
  list_node *n = list_get_node (l, index);
  if (n == NULL) return (NULL);
  return (n->data);
}

//=============================================================================
//=============================================================================
list_node *list_get_node (list *l, int index)
{
  int i = 0;
  list_node *iter = l->head;

  if (index < 0 || index > l->length - 1) return (NULL);
  for (i = 0; i < index && iter != NULL; i++) iter = iter->next;
  return (iter);
}

//=============================================================================
//=============================================================================
void list_iterate (list *l, void (*iterator)(void *))
{
  list_node *n = NULL;
  if (iterator == NULL) return;
  n = list_get_iter (l);
  while (list_iter_hasmore (n))
    {
      iterator (n->data);
      list_iter_advance (n);
    }
}

//=============================================================================
//=============================================================================
void *list_remove (list *l, int index)
{
  list_node *node = NULL;
  void *retval = NULL;
  node = list_get_node (l, index);
  if (node == NULL) return (NULL);

  retval = node->data;
  if (node->prev != NULL) node->prev->next = node->next;
  else l->head = node->next;

  if (node->next != NULL) node->next->prev = node->prev;
  else l->tail = node->prev;
  
  l->length--;
  free (node);
  return (retval);
}
